package validator

import (
	"context"
	"github.com/ecodeclub/ekit/slice"
	"gorm.io/gorm"
	"time"
	"webook/pkg/logger"
	"webook/pkg/migrator/events"
)

func (v *Validator[T]) validateBaseToTargetBatch(ctx context.Context) error {
	offset := 0
	limit := v.batchSize
	for {
		var srcs []T
		srcs, err := v.fromBaseBatch(ctx, offset, limit)
		if err == gorm.ErrRecordNotFound || len(srcs) == 0 {
			//增量校验时候要考虑一直运行 没有数据的时候 要不要return
			if v.sleepInterval <= 0 {
				return nil
			}
			time.Sleep(v.sleepInterval)
			continue
		}
		if err != nil {
			v.l.Error("base -> target 查询base失败", logger.Error(err))
			offset += len(srcs)
			continue
		}
		//正常情况
		var dsts []T
		ids := slice.Map(srcs, func(idx int, t T) int64 {
			return t.ID()
		})
		err = v.target.WithContext(ctx).Where("id in ?", ids).Find(&dsts).Error
		if err == gorm.ErrRecordNotFound {
			//一条数据都没有的话
			v.notifyTargetMissing(srcs)
			offset += len(srcs)
			continue
		}
		if err != nil {
			v.l.Error("base -> target 查询base失败", logger.Int64s("ids", ids), logger.Error(err))
			offset += len(srcs)
			continue
		}

		//对比
		dstsMp := make(map[int64]T, len(dsts))
		for _, elem := range dsts {
			dstsMp[elem.ID()] = elem
		}
		for _, src := range srcs {
			if dst, ok := dstsMp[src.ID()]; !ok {
				v.notify(src.ID(), events.InconsistentEventTypeTargetMissing)
			} else if !src.CompareTo(dst) {
				v.notify(src.ID(), events.InconsistentEventTypeNEQ)
			}
		}
		if len(srcs) < limit {
			if v.sleepInterval <= 0 {
				return nil
			}
			time.Sleep(v.sleepInterval)
			continue
		}
		offset += len(srcs)
	}
	return nil
}
func (v *Validator[T]) notifyTargetMissing(ts []T) {
	for _, elem := range ts {
		v.notify(elem.ID(), events.InconsistentEventTypeTargetMissing)
	}
}
func (v *Validator[T]) fullFromBaseBatch(ctx context.Context, offset, limit int) ([]T, error) {
	dbCtx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
	var srcs []T
	err := v.base.WithContext(dbCtx).Order("id").Offset(offset).Limit(limit).Find(&srcs).Error
	return srcs, err
}
func (v *Validator[T]) incrFromBaseBatch(ctx context.Context, offset, limit int) ([]T, error) {
	dbCtx, cancel := context.WithTimeout(ctx, time.Second)
	defer cancel()
	var srcs []T
	err := v.base.WithContext(dbCtx).Order("utime").Where("utime > ?", v.utime).
		Offset(offset).Limit(limit).Find(&srcs).Error
	return srcs, err
}
